#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import uuid
import logging
import asyncio
from typing import Dict, Any, Optional

# 导入华为云OBS SDK
try:
    from obs import ObsClient
except ImportError:
    ObsClient = None

logger = logging.getLogger(__name__)

class OBSUploader:
    """华为云OBS图片上传器
    
    处理图片上传到华为云OBS存储。
    """
    
    def __init__(self, config: Dict[str, Any]):
        """初始化OBS上传器
        
        Args:
            config: 配置字典
        """
        self.config = config
        self.obs_config = config["obs"]
        
        # 初始化OBS客户端
        self.client = None
        if ObsClient:
            try:
                self.client = ObsClient(
                    access_key_id=self.obs_config["access_key"],
                    secret_access_key=self.obs_config["secret_key"],
                    server=self.obs_config["endpoint"]
                )
                logger.info("已初始化OBS客户端")
            except Exception as e:
                logger.error(f"初始化OBS客户端失败: {str(e)}")
                raise
    
    async def upload(self, image: Dict[str, Any]) -> Optional[str]:
        """上传图片到OBS
        
        Args:
            image: 图片信息字典，包含以下键:
                - path: 图片文件路径（可选，与data二选一）
                  或指定OBS上的目标路径（当包含data但不存在于本地文件系统）
                - filename: 图片文件名（可选）
                - format: 图片格式（可选）
                - data: 图片二进制数据（可选，与path二选一）
            
        Returns:
            str: 上传后的URL，如果上传失败则返回None
            
        Raises:
            RuntimeError: 如果OBS客户端未初始化
            ValueError: 如果图片信息不完整
            Exception: 其他上传错误
        """
        if not self.client:
            logger.error("OBS客户端未初始化，无法上传图片")
            raise RuntimeError("OBS客户端未初始化")
        
        try:
            # 检查是否有明确指定的对象键
            object_key = None
            if "object_key" in image:
                object_key = image["object_key"]
                logger.info(f"使用指定的对象键: {object_key}")
            # 检查是否存在自定义OBS路径
            elif "path" in image and "data" in image and not os.path.exists(image["path"]):
                # 如果路径不存在但有data字段，则视为自定义OBS路径
                object_key = image["path"]
                logger.info(f"检测到自定义OBS路径: {object_key}")
            
            # 优先使用data字段的图片数据
            if "data" in image and image["data"]:
                # 确定对象键
                if not object_key:
                    # 生成默认对象名
                    if "filename" in image:
                        filename = image["filename"]
                    else:
                        # 使用随机文件名和格式后缀
                        filename = f"{uuid.uuid4().hex}.{image.get('format', 'png')}"
                    
                    object_key = f"images/{filename}"
                
                logger.info(f"上传图片到OBS，对象键: {object_key}")
                    
                # 从内存数据直接上传
                loop = asyncio.get_event_loop()
                resp = await loop.run_in_executor(
                    None,
                    lambda: self.client.putObject(
                        bucketName=self.obs_config["bucket"],
                        objectKey=object_key,
                        content=image["data"],
                        headers={'acl': 'public-read'}  # 设置对象ACL为公共读
                    )
                )
            elif "path" in image and os.path.exists(image["path"]):
                # 获取图片路径
                image_path = image["path"]
                
                # 如果没有指定对象键，则生成默认值
                if not object_key:
                    object_key = f"images/{uuid.uuid4().hex}_{os.path.basename(image_path)}"
                
                # 从文件路径上传
                loop = asyncio.get_event_loop()
                resp = await loop.run_in_executor(
                    None,
                    lambda: self.client.putFile(
                        bucketName=self.obs_config["bucket"],
                        objectKey=object_key,
                        file_path=image_path,
                        headers={'acl': 'public-read'}  # 设置对象ACL为公共读
                    )
                )
            else:
                logger.error("图片数据不完整，需要提供path或data")
                raise ValueError("图片数据不完整，需要提供path或data")
            
            # 检查上传结果
            if resp.status < 300:
                # 构建图片URL
                url = f"https://{self.obs_config['bucket']}.{self.obs_config['endpoint']}/{object_key}"
                logger.info(f"图片已上传到OBS: {url}")
                return url
            else:
                logger.error(f"上传图片到OBS失败: {resp.status}, {resp.errorCode}, {resp.errorMessage}")
                return None
                
        except Exception as e:
            logger.error(f"上传图片到OBS出错: {str(e)}")
            raise
    
    async def close(self):
        """关闭OBS客户端"""
        if self.client:
            try:
                loop = asyncio.get_event_loop()
                await loop.run_in_executor(None, self.client.close)
                logger.info("已关闭OBS客户端")
            except Exception as e:
                logger.error(f"关闭OBS客户端出错: {str(e)}")
    
    def __del__(self):
        """析构函数，确保关闭客户端"""
        if self.client:
            self.client.close() 